package com.tyq.auth.oauth2;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;

/**
 * 授权服务器配置
 *
 * @author 谭永强
 * @date 2024-05-20
 */
@Configuration
@EnableAuthorizationServer
public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private TokenStore tokenStore;
    @Resource
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Resource
    private ClientDetailsService clientDetailsService;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private AuthorizationCodeServices authorizationCodeServices;

    /**
     * 自定义客户端信息
     *
     * @param clients 客户端信息
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //放在内存
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        builder
                //配置clientId
                .withClient("Browser")
                //配置client-secret
                .secret(passwordEncoder.encode("BrowserSecret"))
                //资源列表
                .resourceIds("order")
                //配置access-token的过期时间
                .accessTokenValiditySeconds(3600)
                //配置refresh-token的过期时间
                .refreshTokenValiditySeconds(7200)
                //重定向地址，用于授权成功后跳转
                .redirectUris("https://www.baidu.com")
                //自动授权，默认为false，需要手动授权
                .autoApprove(true)
                //配置授权类型
                //authorization_code:授权码模式
                //password:密码模式
                //client_credentials:客户端模式
                //implicit:简化模式，不会返回授权码，直接返回accessToken
                //refresh_token:刷新token模式，不配置则登录时不会返回refreshToken
                .authorizedGrantTypes("password", "refresh_token")
                //配置授权范围
                .scopes("all");
    }

    /**
     * 自定义令牌访问端点和令牌管理服务
     *
     * @param endpoints 端点
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                //自定义令牌管理服务，在令牌服务器中指定令牌存储策略和令牌转换器
                .tokenServices(tokenServices())
                //密码模式需要
                .authenticationManager(authenticationManager)
                //授权码模式需要
                .authorizationCodeServices(authorizationCodeServices)
                //该字段设置设置refresh_token是否重复使用
                .reuseRefreshTokens(false);
    }

    /**
     * 令牌管理服务
     *
     * @return 令牌管理服务
     */
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        //令牌存储策略
        tokenServices.setTokenStore(tokenStore);
        //令牌增强器：配置使用jwt作为令牌生成器
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter);
        //客户端信息服务
        tokenServices.setClientDetailsService(clientDetailsService);
        //是否产生刷新令牌
        tokenServices.setSupportRefreshToken(true);
        //重用刷新令牌
        tokenServices.setReuseRefreshToken(false);
        //默认令牌有效期
        tokenServices.setAccessTokenValiditySeconds(3600);
        //默认刷新令牌有效期
        tokenServices.setAccessTokenValiditySeconds(7200);
        return tokenServices;
    }

    /**
     * 自定义令牌端点的安全策略
     *
     * @param security 安全策略
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                //允许表单认证
                .allowFormAuthenticationForClients()
                //允许访问/oauth/token_key，提供公有秘钥的端点，如果要使用JWT的话。
                .tokenKeyAccess("permitAll()")
                //允许访问/oauth/check_token，用于资源服务访问的令牌解析。
                .checkTokenAccess("permitAll()");
    }
}
